#include "clientthread.h"

#include <QByteArray>
#include <QSslKey>
#include <QSslCertificate>
#include <QFile>
#include <QApplication>

#include "core/core.h"

#include <QDebug>
#include <exception>

ClientThread::ClientThread(QObject *parent) :
    QThread(parent)
{
	m_clientsCount = 0;
}

ClientThread::~ClientThread(){
	while( m_clientsCount ){
		asm("NOP");
	}

	quit();
	wait();
	qDebug() << Q_FUNC_INFO;
}

void ClientThread::unbindSocket(){
	qDebug() << m_clientsCount;
	m_clientsCount--;
}

void ClientThread::bindSocket(int socketDescriptor){
	qDebug() << this << thread() << currentThread();
	ClientSocket* socket;
	try{
		socket = new ClientSocket();
	}catch(std::bad_alloc&){
		qWarning() << Q_FUNC_INFO <<"Cannot create new socket";
		return;
	}

	m_clientsCount++;

	socket->moveToThread( thread() );
	connect( qApp, SIGNAL(aboutToQuit()), socket, SLOT(deleteLater()));

	connect( socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()) );
	connect( socket, SIGNAL(destroyed()), this, SLOT(unbindSocket()) );

	_connectSocketSignals(socket);

	if( !socket->setSocketDescriptor( socketDescriptor ) ) {
		qWarning("couldn't set socket descriptor");
		delete socket;
		return;
	}
	_startServerEncryption(socket);
}


void ClientThread::_startServerEncryption ( ClientSocket *socket ){

	QFile file;
	if( Core::instance()->settings()->value( "general/network/SSLKeyDefault",
											 QVariant(true) ).toBool() )
	{
		file.setFileName( ":/SSL/server.key" );
	}else{
		QString tmp = Core::instance()->settings()->value(
				"general/network/SSLKey", QVariant() ).toString();

		file.setFileName( tmp );
	}

	if( !file.open( QIODevice::ReadOnly ) ){
		qWarning( "SSLServer => could'n open %s", qPrintable(file.fileName()) );
		socket->disconnectFromHost();
		return;
	}

	QSslKey key(&file, QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey);

	if( key.isNull() ){
		qWarning( "SSLServer => key is null" );
		socket->disconnectFromHost();
		return;
	}

	socket->setPrivateKey(key);


	if( Core::instance()->settings()->value( "general/network/SSLCertDefault",
											 QVariant(true) ).toBool() )
	{
		socket->setLocalCertificate( ":/SSL/server.crt" );
	}else{
		QString tmp = Core::instance()->settings()->value(
				"general/network/SSLCert", QVariant() ).toString();

		socket->setLocalCertificate( tmp );
	}

	if( !socket->localCertificate().isValid() ){
		qWarning( "SSLServer => local certificate is not valid" );
		socket->disconnectFromHost();
		return;
	}


	//nie mam domyślnie CA, czyli certyfikat self-signed
	if( !Core::instance()->settings()->value("general/network/SSLCertCADefault",
											 QVariant(true) ).toBool() )
	{
		QString tmp = Core::instance()->settings()->value(
				"general/network/SSLCertCA", QVariant() ).toString();

		socket->addCaCertificates( tmp );
	}

	socket->startServerEncryption();

}




void ClientThread::_connectSocketSignals( ClientSocket* socket ){
	connect(socket, SIGNAL(encrypted()), this, SLOT(slot_encrypted()));
	connect(socket, SIGNAL(encryptedBytesWritten(qint64)),
			this, SLOT(slot_encryptedBytesWritten(qint64)));
	connect(socket, SIGNAL(modeChanged(QSslSocket::SslMode)),
			this, SLOT(slot_modeChanged(QSslSocket::SslMode)));
	connect(socket, SIGNAL(peerVerifyError(const QSslError &)),
			this, SLOT(slot_peerVerifyError (const QSslError &)));


	//connect(socket,SIGNAL(sslErrors(const QList<QSslError> &)),
	//		socket, SLOT(ignoreSslErrors(const QList<QSslError> &)));
	connect(socket, SIGNAL(sslErrors(const QList<QSslError> &)),
		  this, SLOT(slot_sslErrors(const QList<QSslError> &)));

	connect(socket, SIGNAL(connected()),
			this, SLOT(slot_connected()));
	connect(socket, SIGNAL(disconnected()),
			this, SLOT(slot_disconnected()));
	connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
			this, SLOT(slot_error(QAbstractSocket::SocketError)));
	connect(socket, SIGNAL(hostFound()),
			this, SLOT(slot_hostFound()));

	connect(socket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &,
													   QAuthenticator *)),
			this, SLOT(slot_proxyAuthenticationRequired(const QNetworkProxy &,
														QAuthenticator *)));

	connect(socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
			this, SLOT(slot_stateChanged(QAbstractSocket::SocketState)));


	connect( socket, SIGNAL(dataReaded(ClientSocket*,QByteArray)),
			 &m_parser, SLOT(decodeData(ClientSocket*,QByteArray)) );
}

void ClientThread::slot_encrypted(){
	qDebug("ClientThread::slot_encrypted");
//	ClientSocket *socket = qobject_cast<ClientSocket*>(sender());
//	if( socket )
//		socket->write("działa");
}

void ClientThread::slot_encryptedBytesWritten(qint64 written){
	qDebug("ClientThread::slot_encryptedBytesWritten(%ld)", (long) written);
}

void ClientThread::slot_modeChanged(QSslSocket::SslMode mode){
	qDebug("ClientThread::slot_modeChanged(%d)", mode);
}

void ClientThread::slot_peerVerifyError(const QSslError &a){
	qDebug("ClientThread::slot_peerVerifyError");
	qDebug()<<a;
}

void ClientThread::slot_sslErrors(const QList<QSslError> &a){
	ClientSocket *socket = qobject_cast<ClientSocket*>(sender());
	if( socket )
		socket->ignoreSslErrors();
	qDebug("ClientThread::slot_sslErrors");
	qDebug()<<a;
}

//void ClientThread::slot_readyRead(){
//	qDebug("ClientThread::slot_readyRead");
//	ClientSocket *socket = qobject_cast<ClientSocket*>(sender());
//	qDebug() << socket->readAll();
//}

void ClientThread::slot_connected(){
	qDebug("ClientThread::slot_connected");
}

void ClientThread::slot_disconnected(){
	qDebug("ClientThread::slot_disconnected");
}

void ClientThread::slot_error(QAbstractSocket::SocketError err){
	qDebug() << "ClientThread::slot_error(" << err << ")";
	ClientSocket *socket = qobject_cast<ClientSocket*>(sender());
	if( socket )
		qDebug() << socket->errorString();
}

void ClientThread::slot_hostFound(){
	qDebug("ClientThread::slot_hostFound");
}

void ClientThread::slot_proxyAuthenticationRequired(const QNetworkProxy &,
												 QAuthenticator *)
{
	qDebug("ClientThread::slot_proxyAuthenticationRequired");
}

void ClientThread::slot_stateChanged(QAbstractSocket::SocketState state){
	qDebug() << "ClientThread::slot_stateChanged(" << state << ")";
}

